package org.zjvis.graph.analysis.service.aspect;

import cn.hutool.core.bean.BeanUtil;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.zjvis.datascience.common.annotation.ProjectRoleAuth;
import org.zjvis.graph.analysis.service.GraphAnalysisService;
import org.zjvis.graph.analysis.service.GraphFilterPipelineService;
import org.zjvis.graph.analysis.service.GraphFilterService;
import org.zjvis.graph.analysis.service.annotation.*;
import org.zjvis.graph.analysis.service.exception.GraphAnalysisException;
import org.zjvis.graph.analysis.service.enums.ApiResultCode;
import org.zjvis.graph.analysis.service.http.ProjectService;
import org.zjvis.graph.analysis.service.http.UserService;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;

@Component
@Aspect
public class AuthAspect {

    @Autowired
    private GraphAnalysisService graphAnalysisService;

    @Autowired
    private GraphFilterPipelineService graphFilterPipelineService;

    @Autowired
    private GraphFilterService graphFilterService;

    @Autowired
    private ProjectService projectService;

    @Autowired
    private UserService userService;

    @Pointcut("execution(* org.zjvis..web.controller.GraphAnalysisController.*(..))")
    public void controller() {
    }

    @Before("controller()")
    public void checkAuth(JoinPoint joinPoint) throws Throwable {
        //cookie检查
        userService.getCurrentUserId();
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Object[] params = joinPoint.getArgs();
        Annotation[][] annotations = ms.getMethod().getParameterAnnotations();
        for (int i = 0; i < annotations.length; i++) {
            int j = 0;
            Object obj = params[i];
            Map<String, Long> checkRelationMap = new HashMap<>();
            for (; j < annotations[i].length; j++) {
                Annotation annotation = annotations[i][j];
                if (annotation instanceof ProjectRoleAuth) {
                    checkProjectRoleAuth(obj, (ProjectRoleAuth) annotation);
                    checkRelationMap.put("projectId", getLongValue(obj, ((ProjectRoleAuth) annotation).field()));
                }
                else if (annotation instanceof GraphAuth) {
                    checkGraphAuth(obj, (GraphAuth) annotation);
                    checkRelationMap.put("graphId", getLongValue(obj, ((GraphAuth) annotation).field()));
                }
                else if (annotation instanceof GraphFilterPipelineAuth) {
                    checkGraphFilterPipelineAuth(obj, (GraphFilterPipelineAuth) annotation);
                    checkRelationMap.put("graphFilterPipelineId", getLongValue(obj, ((GraphFilterPipelineAuth) annotation).field()));
                }
                else if (annotation instanceof GraphFilterAuth) {
                    checkGraphFilterAuth(obj, (GraphFilterAuth) annotation);
                    checkRelationMap.put("graphFilterId", getLongValue(obj, ((GraphFilterAuth) annotation).field()));
                }
                else if (annotation instanceof CheckNull) {
                    checkNull(obj, (CheckNull) annotation);
                }
                else if (annotation instanceof CheckNulls) {
                    checkNulls(obj, (CheckNulls) annotation);
                }
            }
            // check从属关系
            if (checkRelationMap.containsKey("projectId") && checkRelationMap.containsKey("graphId")) {
                graphAnalysisService.checkGraphAndProject(checkRelationMap.get("graphId"), checkRelationMap.get("projectId"));
            }
            if (checkRelationMap.containsKey("graphId") && checkRelationMap.containsKey("graphFilterPipelineId")) {
                graphFilterPipelineService.checkPipelineAndGraph(checkRelationMap.get("graphFilterPipelineId"), checkRelationMap.get("graphId"));
            }
            if (checkRelationMap.containsKey("graphFilterId")) {
                graphFilterService.checkFilterAndPipeline(checkRelationMap.get("graphFilterId"), checkRelationMap.get("graphFilterPipelineId"), checkRelationMap.get("graphId"));
            }
        }
    }

    private void checkProjectRoleAuth(Object obj, ProjectRoleAuth projectRoleAuth) {
        if (obj == null) {
            return;
        }
        Object value;
        if (StringUtils.EMPTY.equals(projectRoleAuth.field())) {
            value = obj;
        } else {
            value = BeanUtil.getFieldValue(obj, projectRoleAuth.field());
        }

        if (value == null) {
            throw new GraphAnalysisException(ApiResultCode.PARAM_ERROR, String.format("需要传入%s校验权限!", projectRoleAuth.field()));
        }

        if (value instanceof String) {
            try {
                value = Long.valueOf((String)value);
            } catch (Exception ignored) {
            }
        }
        if (value instanceof Integer) {
            try {
                value = Long.valueOf(value.toString());
            } catch (Exception ignored) {
            }
        }
        if (!(value instanceof Long)) {
            throw new GraphAnalysisException(ApiResultCode.PARAM_ERROR,
                    String.format("%s字段有误:非长整型.字段名:%s, value:%s, type is %s",
                            projectRoleAuth.field(),
                            projectRoleAuth.field(),
                            value, value.getClass()));
        }
        Long projectId = ((Long) value);
        projectService.checkRoleAuth(projectId, projectRoleAuth.role().getValue());
    }


    private void checkGraphAuth(Object obj, GraphAuth graphAuth) {
        if (obj == null) {
            return;
        }
        Object value;
        if (StringUtils.EMPTY.equals(graphAuth.field())) {
            value = obj;
        } else {
            value = BeanUtil.getFieldValue(obj, graphAuth.field());
        }

        if (value == null) {
            throw new GraphAnalysisException(ApiResultCode.PARAM_ERROR, String.format("需要传入%s校验权限!", graphAuth.field()));
        }

        if (value instanceof String) {
            try {
                value = Long.valueOf((String)value);
            } catch (Exception ignored) {
            }
        }
        if (value instanceof Integer) {
            try {
                value = Long.valueOf(value.toString());
            } catch (Exception ignored) {
            }
        }
        if (!(value instanceof Long)) {
            throw new GraphAnalysisException(ApiResultCode.PARAM_ERROR,
                    String.format("%s字段有误:非长整型.字段名:%s, value:%s, type is %s",
                            graphAuth.field(),
                            graphAuth.field(),
                            value, value.getClass()));
        }
        Long graphId = ((Long) value);
        graphAnalysisService.checkAuth(graphId);
    }

    private void checkGraphFilterPipelineAuth(Object obj, GraphFilterPipelineAuth graphFilterPipelineAuth) {
        if (obj == null) {
            return;
        }
        Object value;
        if (StringUtils.EMPTY.equals(graphFilterPipelineAuth.field())) {
            value = obj;
        } else {
            value = BeanUtil.getFieldValue(obj, graphFilterPipelineAuth.field());
        }

        if (value == null) {
            throw new GraphAnalysisException(ApiResultCode.PARAM_ERROR, String.format("需要传入%s校验权限!", graphFilterPipelineAuth.field()));
        }

        if (value instanceof String) {
            try {
                value = Long.valueOf((String)value);
            } catch (Exception ignored) {
            }
        }
        if (value instanceof Integer) {
            try {
                value = Long.valueOf(value.toString());
            } catch (Exception ignored) {
            }
        }
        if (!(value instanceof Long)) {
            throw new GraphAnalysisException(ApiResultCode.PARAM_ERROR,
                    String.format("%s字段有误:非长整型.字段名:%s, value:%s, type is %s",
                            graphFilterPipelineAuth.field(),
                            graphFilterPipelineAuth.field(),
                            value, value.getClass()));
        }

        Long id = ((Long) value);
        graphFilterPipelineService.checkAuth(id);

    }

    private void checkGraphFilterAuth(Object obj, GraphFilterAuth graphFilterAuth) {
        if (obj == null) {
            return;
        }
        Object value;
        if (StringUtils.EMPTY.equals(graphFilterAuth.field())) {
            value = obj;
        } else {
            value = BeanUtil.getFieldValue(obj, graphFilterAuth.field());
        }

        if (value == null) {
            throw new GraphAnalysisException(ApiResultCode.PARAM_ERROR, String.format("需要传入%s校验权限!", graphFilterAuth.field()));
        }

        if (value instanceof String) {
            try {
                value = Long.valueOf((String)value);
            } catch (Exception ignored) {
            }
        }
        if (value instanceof Integer) {
            try {
                value = Long.valueOf(value.toString());
            } catch (Exception ignored) {
            }
        }
        if (!(value instanceof Long)) {
            throw new GraphAnalysisException(ApiResultCode.PARAM_ERROR,
                    String.format("%s字段有误:非长整型.字段名:%s, value:%s, type is %s",
                            graphFilterAuth.field(),
                            graphFilterAuth.field(),
                            value, value.getClass()));
        }

        Long id = ((Long) value);
        graphFilterService.checkAuth(id);
    }

    private void checkNull(Object obj, CheckNull checkNull) {
        if (obj == null) {
            return;
        }
        Object value;
        if (StringUtils.EMPTY.equals(checkNull.field())) {
            value = obj;
        } else {
            value = BeanUtil.getFieldValue(obj, checkNull.field());
        }
        if (value == null || StringUtils.EMPTY.equals(value)) {
            throw new GraphAnalysisException(ApiResultCode.PARAM_ERROR,
                    String.format("%s=null", checkNull.field())
            );
        }
    }

    private void checkNulls(Object obj, CheckNulls checkNulls) {
        for (CheckNull checkNull: checkNulls.value()) {
            checkNull(obj, checkNull);
        }
    }

    private Long getLongValue(Object obj, String field) {
        Object value;
        if (StringUtils.EMPTY.equals(field)) {
            value = obj;
        } else {
            value = BeanUtil.getFieldValue(obj, field);
        }
        try {
            return Long.valueOf(value.toString());
        } catch (NumberFormatException e) {
            throw new GraphAnalysisException(ApiResultCode.PARAM_ERROR,
                    String.format("%s=null", field)
            );
        }
    }

}
